#
# Handles drawing the arrows for explaining cross-multiplication, etc.
# Takes a populated counting board object and inserts code for arrows
#
# 2009, Mike Widner <mikewidner@gmail.com>
#
########

package Fangcheng::LaTeX::CountingBoard::Arrows;

use 5.006001;
use strict;
use warnings;
use vars qw( $AUTOLOAD @ISA );

require Fangcheng::LaTeX::CountingBoard;

@ISA = qw( Fangcheng::LaTeX::CountingBoard );

use Fangcheng::Matrix;

my $VERSION = '0.01';

sub execute {
	my ($self, $matrix, $prev, $board, $calc, $step) = @_;

	if ( $step == 0 ) {
		return();
	}
	
	if ( $calc->lastStep == $step ) {
		$self->board( undef );
		return();
	}

	if ( not defined $self->arrow ) {
		$self->arrow( Fangcheng::Matrix->new( $board->board->rows, '' ) );
	}
	
	# so as not to overwrite the values in CountingBoard object
	# copy all our matrices to internal ones
	$self->width( $board->width );
	$self->grid( $board->grid );
	$self->marks( $board->marks );
	$self->board( $board->board );

	# do the actual work
	if ( $calc->getStage( $step ) == 1 ) {
		$self->setNegatives( $matrix, $step );
	}
	elsif ( $calc->getStage( $step ) == 2 ) {
		$self->crossMultiply( $matrix, $prev, $step );	
	}

	$self->makeGrid( $matrix );

	return( $self );
}


sub wrapString {
	my ($self, $str) = @_;
	my $head = '';
	$head .= qq|\\dpi{100}\\def\\zN{|;
	$head .= $self->board->rows();
	$head .= qq|}\\def\\zL{|;
	$head .= $self->board->rows() - 1;
	$head .= qq|}\\fbox{\\xymatrix \@H= 5mm {|;
	return( $head . $str . '}}' );
}

sub toString {
	my ($self) = @_;

	if ( not defined $self->board ) {
		return( undef );
	}
	my $str = '';
	foreach my $col ( 1 .. $self->board->columns ) {
		foreach my $row ( reverse( 1 .. $self->board->rows ) ) {
			$str .= $self->board->get( $row, $col );
		}
	}
	return( $self->wrapString( $str ) );
}

#sub description {
#	my ($self, $stage, $step) = @_;	
#	return( $description[$stage][$step] );
#}

### down(), left(), right(), and up() take the number of times we should go a direction
### and return the proper string

sub _dirTimes {
	my ($self, $times, $direction) = @_;
	return( $direction x $times );
}

sub up {
	my ($self, $times) = @_;
	return( $self->_dirTimes( $times, 'u') );
}

sub down {
	my ($self, $times) = @_;
	return( $self->_dirTimes( $times, 'd') );
}

sub left {
	my ($self, $times) = @_;
	return( $self->_dirTimes( $times, 'l') );
}

sub right {
	my ($self, $times) = @_;
	return( $self->_dirTimes( $times, 'r') );	
}

sub curve {
	my ($self, $i, $nRows) = @_;
	return( 2 * ($nRows - $i) . 'mm' );
}

### NOTE: Each arrow* function takes $i, $nRows, then the directions in order of function name
### e.g. arrowDownLeft needs down first, then left, then curve and offset
sub arrowDownLeft {
	my ($self, $i, $nRows, $down, $left, $curve, $offset) = @_;
		
	if ( not defined $curve ) {
		$curve = $self->curve( $i, $nRows );
	}
	
	if ( not defined $offset ) {
		$offset = '+UR';
	}

	return( qq|\\POS[]+L+<2mm,0mm>\\ar\@/_$curve/[$left$down]$offset| );
}

sub arrowDown {
	my ($self, $i, $nRows, $down, $curve, $offset) = @_;
	
	if ( not defined $offset ) {
		$offset = '+R';
	}
	if ( not defined $curve ) {
		$curve = $self->curve( $i, $nRows );
	}
	return( qq|\\POS[]+R\\ar@/^$curve/[$down]$offset|)
}

sub arrowLeft {
	my ($self, $i, $nRows, $left, $curve, $offset) = @_;

	if ( not defined $curve ) {
		$curve = $self->curve( $i, $nRows );
	}
	return( qq|\\POS[]+L+<1mm,0mm>\\ar\@/^$curve/[$left]+R| );
}

sub arrowDownRight {
	my ($self, $i, $nRows, $down, $right, $curve, $offset) = @_;
	
	if ( $i <= $nRows - 2 ) {
		$offset = '+UL';
	}
	else {
		$offset = '+U';
	}	
	$curve = $self->curve( $i, $nRows );		
	return( qq|\\POS[]+R\\ar\@/^$curve/[$down$right]$offset| );
}

sub arrowUpLeft {
	my ($self, $i, $nRows, $up, $left, $curve, $offset) = @_;

	if ( not defined $offset ) {
		$offset = '5mm';
	}

	return( qq|\\POS[]+U\\ar@/_$offset/[$up$left]| );
}

####### These next three functions correspond to the three steps in Calc::Determinants

# first stage of the determinantal solution
sub setNegatives {
	my ($self, $matrix, $i) = @_;
	my $nRows = $matrix->rows;

	$self->marks->set($nRows, $i, $self->arrowDown( $i, $nRows, $self->down(1) ) );

	# Arrow back left, up one:
	my $left = $self->left( $nRows - $i );
	$self->marks->set($i, $i + 1, $self->arrowUpLeft( $i, $nRows, $self->up(1), $left ));
	$self->drawBox( $nRows, $i );
	$self->drawBox( $i, $i + 1 );
	$self->drawBox( $nRows, $i + 1 );

	$self->describeSetNegatives( $i, $nRows, $matrix );
}

sub describeSetNegatives {
	my ($self, $i, $nRows, $matrix) = @_;
	my $val1 = $matrix->get( $nRows, $i );
	my $val2 = $matrix->get( $i, $i + 1 );
	my $sum = $val1 * $val2;
	my $str = "We multiply the entry located in <i>row $i, column 1</i> (<b>";
	$str .= $val1;
	$str .= "</b>) by the entry in <i>row " . ($i + 1) . " column " . ($matrix->columns - $i) . "</i> (<b>";
	$str .= $val2;
	$str .= '</b>), change the sign, and place the result (<b>';
	$str .= -1 * $val1 * $val2;
	$str .= '</b>) in <i>row '. ($i + 1) . ' column 1</i>:'; 
	$self->description( $str );	
}

sub description {
	my ($self, $str) = @_;
	if ( defined $str ) {
		$self->{'_description'} = $str;	
	}
	return( $self->{'_description'} );
}

# second stage of determinantal solution
sub crossMultiply {
	my ($self, $matrix, $prev, $step) = @_;
	my $nRows = $matrix->rows;
	my $nCols = $matrix->columns;

	my $i = 2 - ( $nRows - $step );		# increment through the row/col
	
	my $left = $self->left( $nRows - $i );
	my $right = $self->right( $nRows - $i );
	my $down = $self->down( $nCols - $i );
		
	$self->marks->set($i, $i, $self->arrowDownLeft( $i, $nRows, $down, $left ) );
	$self->drawBox( $i, $i );

	$self->marks->set( $nRows, $i, $self->arrowDownRight( $i, $nRows, $down, $right ) );
	$self->drawBox( $nRows, $i );
	
	$self->marks->set( $i, $nCols, $self->arrowLeft( $i, $nRows, $left ) );
	$self->drawBox( $i, $nCols );
	$self->drawBox( $nRows, $nCols );
	
	$self->describeCrossMultiply( $i, $nRows, $matrix, $prev );
}

sub describeCrossMultiply {
	my ($self, $i, $nRows, $matrix, $prev) = @_;
	my $nCols = $matrix->columns;
	my $val1 = $prev->get( $nRows, $nCols );
	my $val2 = $matrix->get( $i, $i );
	my $val3 = $matrix->get( $i, $nCols );
	my $val4 = $matrix->get( $nRows, $i );
	my $result = ($val2 * $val1) - ($val3 * $val4);
	
	my $row = $nCols - $i + 1;
	my $col = $nRows - $i + 1;
	
	my $str = 'Now we perform a kind of "cross-multiplication":<br>';
	$str .= "(1) We begin with the entry located in <i>row $nCols column 1</i>";
	$str .= ", which at the beginning of this calculation, from the previous step, was originally <b>$val1</b>.<br>";
	$str .= "(2) We then multiply this entry by the entry in <i>row $i, column $col</i>, which is <b>$val2</b>";
	$str .= ", giving <b>$val1 * $val2</b>, and replace the entry in <i>row $nCols, column 1</i> with this temporary result, <b>";
	$str .= $val2 * $val1;
	$str .= "</b><br>";
	$str .= "(3) We then multiply the entry in <i>row $nCols, column $col</i> ($val3)";
	$str .= " by the entry in <i>row 1, column $i</i> (<b>$val4</b>), giving <b>$val3 * $val4 = " . $val3 * $val4 . '</b>.<br>';
	$str .= "(4) Finally, we subtract the result from the previous step, <b>" . $val3 * $val4 . "</b>, from the temporary entry ";
	$str .= "currently in <i>row $nCols, column 1</i> <b>";
	$str .= ($val2 * $val1) . ' - ' . ($val3 * $val4);
	$str .= '</b>, and then replace the temporary entry in <i>';
	$str .= "row $nCols, column 1</i> with the result <b>$result</b>.";
	$self->description( $str );
}

# third stage, currently does nothing; handled by formula module
sub divisor {
	my ($self, $matrix, $step) = @_;
	# This function currently empty
	# Older code returns nothing for this stage
	# but leaving function in case we need to add something
}

# not all 'm's so we need to override parent's method
# we switch 'm' to 'f' for every field we're calculating with
sub drawBox {
	my ($self, $row, $col) = @_;
	$self->grid->set( $row, $col, 'f' );
}
1;